001 /*
002 * @(#)CanvasWC.java 1.0 06/11/1999
003 */
004
005 package wc;
006
007 import java.awt.*;
008 import java.awt.geom.*;
009 import java.awt.event.*;
010 import java.awt.image.*;
011 import java.awt.font.*;
012 import javax.swing.JPanel;
013
014 /**
015 * This <code>CanvasWC</code> class extends the {@link JPanel} class to
016 * provide control over displaying using a device-independent coordinate
017 * system called World Coordinates (WC), which is used by applications.
018 * Displaying is buffered to a {@link BufferedImage} object (offscreen
019 * image) to emulate double buffering rendering. The <code>BufferedImage</code>
020 * object contains a {@link Graphics2D} object as part of its rendering state.
021 * The <code>Graphics2D</code> object contains an {@link AffineTransform}
022 * object that defines how to convert coordinates from client space (world
023 * coordinates) to device-dependent (raster) coordinates in device space.
024 * <p>
025 * Clients need to extend this class, by creating subclasses, in order to
026 * use it.
027 * <p>
028 * The vertical Y axis of the <code>CanvasWC</code> object is set by default
029 * in downward direction, but clients may specify it in upward direction
030 * when the canvas is created.
031 * <p>
032 * ===================
033 * @see javax.swing.JPanel
034 * @see java.awt.Graphics2D
035 * @see java.awt.image.BufferedImage
036 * @see java.awt.event.MouseListener
037 * @see java.awt.event.MouseMotionListener
038 * @version 1.1 02/08/2000
039 * @author Luiz Fernando Martha
040 */
041 // Change history: Luiz Fernando Martha 17/02/2000:
042 // Added drawText and setTextAlignment methods.
043 // Change history: Luiz Fernando Martha 04/04/2000:
044 // Added setTextOpacity method.
045 // Change history: Luiz Fernando Martha 02/08/2000:
046 // Replaced super-class of CanvasWC from Canvas to
047 // JPanel. This was done because class Canvas
048 // is an AWT class and JPanel is a Swing class.
049 // When CanvasWC was an extension of Canvas, some
050 // conflicts with other Swing UI objects were occuring,
051 // such as a dropdown object was drawn on the back
052 // of the canvas instead of on the front of it.
053 // Commented out method paint. Replaced method
054 // update by method paintComponet (with the same
055 // Graphics g argument).
056
057 public abstract class CanvasWC extends JPanel implements MouseListener, MouseMotionListener
058 {
059 /**
060 * Defines the canvas raster area. This area corresponds to the sizes of the
061 * canvas in pixels.
062 * @serial
063 * @see java.awt.Dimension
064 */
065 protected Dimension raster_area;
066
067 /**
068 * Defines the canvas world coordinate area. This area corresponds to the
069 * window in client space which is visible in the canvas. This window is
070 * adjusted to have the same aspect ratio between height and width of the
071 * canvas (in its current state). When the client specifies the windows,
072 * the adjustment maintains the center of given (raw) window in the same
073 * position. This adjustment may be affected by the canvas vertical
074 * distortion factor.
075 * @serial
076 * @see java.awt.geom.Rectangle2D.Double
077 * @see #vdistortion
078 * @see #setWorld
079 */
080 protected Rectangle2D.Double world_area;
081
082 /**
083 * Defines the original canvas world coordinate area. This area corresponds
084 * to the window in world coordinates which was set by the client. That is,
085 * it corresponds to the world area prior to adjustment with respect to the
086 * canvas aspect ratio between height and width.
087 * @serial
088 * @see #setWorld
089 * @see #world_area
090 */
091 protected Rectangle2D.Double world_raw;
092
093 /**
094 * Field constant used to define the vertical Y axis of the canvas in
095 * downward direction.
096 */
097 public static final int DOWNWARD_Y = 0;
098
099 /**
100 * Field constant used to define the vertical Y axis of the canvas in
101 * upward direction.
102 */
103 public static final int UPWARD_Y = 1;
104
105 /**
106 * Keeps the vertical Y axis direction state. The possible values are
107 * <code>DOWNWARD_Y</code> (default) or <code>UPWARD_Y</code>. The
108 * Y axis direction is specified during creation, when the
109 * <code>CanvasWC</code> constructor is called.
110 * @serial
111 */
112 protected int y_dir;
113
114 /**
115 * BufferedImage object that is used to implement double
116 * buffering rendering. This object defines a Graphics2D
117 * graphics context object used to convert from world coordinates
118 * to raster coordinates.
119 * @serial
120 */
121 private BufferedImage ioff;
122
123 /**
124 * Graphics context object (Graphics) of the offscreen
125 * image used privately for double buffering.
126 * @serial
127 */
128 private Graphics goff;
129
130 /**
131 * Graphics context object (Graphics2D) that contains an
132 * AffineTransform object that defines how to convert
133 * coordinates from client space (world coordinates) to device-dependent
134 * (raster) coordinates in device space.
135 * @serial
136 */
137 private Graphics2D g2off;
138
139 /**
140 * Keeps the WC window vertical distortion factor. This factor specifies
141 * the amount of distortion between the height and width of the world
142 * coordinate area. It is essentially a factor between the vertical and
143 * horizontal scales in client world coordinate space. A factor with
144 * value greater than one will cause a vertical stretching of the
145 * image.
146 * @serial
147 * @see #setVDistortion
148 * @see #world_area
149 */
150 protected double vdistortion;
151
152 /**
153 * Private field that holds a auxiliary point in world coordinates.
154 * @serial
155 */
156 private Point2D.Double pt_world;
157
158 /**
159 * Private field that holds a auxiliary point in raster (device) coordinates.
160 * @serial
161 */
162 private Point pt_raster;
163
164 /**
165 * Private field that holds a mouse event point in world coordinates.
166 * @serial
167 */
168 private Point2D.Double ept_world;
169
170 /**
171 * Private field that holds a mouse event point in raster (device) coordinates.
172 * @serial
173 */
174 private Point ept_raster;
175
176 /**
177 * Field constant used to define transparent text background opacity.
178 */
179 public static final int TRANSPARENT = 0;
180
181 /**
182 * Field constant used to define opaque text background opacity.
183 */
184 public static final int OPAQUE = 1;
185
186 /**
187 * Private field that holds current text alignment parameter.
188 */
189 private int text_opacity = TRANSPARENT;
190
191 /**
192 * Private field that holds text box for opaque displaying.
193 */
194 private Rectangle text_box;
195
196 /**
197 * Field constant used to define text alignment.
198 */
199 public static final int NORTH_WEST = 0;
200
201 /**
202 * Field constant used to define text alignment.
203 */
204 public static final int NORTH = 1;
205
206 /**
207 * Field constant used to define text alignment.
208 */
209 public static final int NORTH_EAST = 2;
210
211 /**
212 * Field constant used to define text alignment.
213 */
214 public static final int SOUTH_WEST = 3;
215
216 /**
217 * Field constant used to define text alignment.
218 */
219 public static final int SOUTH = 4;
220
221 /**
222 * Field constant used to define text alignment.
223 */
224 public static final int SOUTH_EAST = 5;
225
226 /**
227 * Field constant used to define text alignment.
228 */
229 public static final int WEST = 6;
230
231 /**
232 * Field constant used to define text alignment.
233 */
234 public static final int CENTER = 7;
235
236 /**
237 * Field constant used to define text alignment.
238 */
239 public static final int EAST = 8;
240
241 /**
242 * Field constant used to define text alignment.
243 */
244 public static final int BASE_LEFT = 9;
245
246 /**
247 * Field constant used to define text alignment.
248 */
249 public static final int BASE_CENTER = 10;
250
251 /**
252 * Field constant used to define text alignment.
253 */
254 public static final int BASE_RIGHT = 11;
255
256 /**
257 * Private field that holds current text alignment parameter.
258 */
259 private int text_alignment = BASE_LEFT;
260
261 /**
262 * Private field that holds current font that is used to display text.
263 */
264 private Font fnt;
265
266 /**
267 * Font renderer context: private field that is a container for
268 * the information needed to measure text that is displayed.
269 */
270 private FontRenderContext frc;
271
272 /**
273 * Private field that holds an auxiliary identity affine
274 * transformation matrix.
275 */
276 AffineTransform identity;
277
278 /**
279 * Default constructor. Sets up vertical Y axis in downward direction.
280 */
281 public CanvasWC()
282 {
283 this( DOWNWARD_Y );
284 }
285
286 /**
287 * Constructor that sets up WC canvas according to a given orientation.
288 * @param y_dir can either be <code>DOWNWARD_Y</code> or <code>UPWARD_Y</code>
289 * @see #DOWNWARD_Y
290 * @see #UPWARD_Y
291 */
292 public CanvasWC( int y_dir )
293 {
294 this.y_dir = y_dir;
295 vdistortion = 1.0;
296 pt_world = new Point2D.Double();
297 pt_raster = new Point();
298 ept_world = new Point2D.Double();
299 ept_raster = new Point();
300 addMouseMotionListener( this );
301 addMouseListener( this );
302
303 text_box = new Rectangle();
304
305 // Create default container of font size information
306 identity = new AffineTransform();
307 frc = new FontRenderContext( identity, false, false );
308 }
309
310 /**
311 * Redisplay abstract method. Client's concrete class must implement it.
312 * This method is called every time the canvas is updated.
313 * @param g is the 2D graphics context that the client uses to display
314 */
315 public abstract void redraw( Graphics2D g );
316
317 /**
318 * Mouse button pressed abstract method. Client's concrete class must
319 * implement it. This method is called when the user presses a mouse
320 * button in the canvas.
321 * @param e is mouse event object
322 * @param pt is the mouse position in client's floating point world coordinates
323 */
324 public abstract void mousePressedWC( MouseEvent e, Point2D.Double pt );
325
326 /**
327 * Mouse button dragged abstract method. Client's concrete class must
328 * implement it. This method is called when user drags the mouse in the
329 * canvas with a button pressed.
330 * @param e is mouse event object
331 * @param pt is the mouse position in client's floating point world coordinates
332 */
333 public abstract void mouseDraggedWC ( MouseEvent e, Point2D.Double pt );
334
335 /**
336 * Mouse button released abstract method. Client's concrete class must
337 * implement it. This method is called when user the mouse button at the
338 * end of a mouse pressed-dragged-released interaction.
339 * @param e is mouse event object
340 * @param pt is the mouse position in client's floating point world coordinates
341 */
342 public abstract void mouseReleasedWC( MouseEvent e, Point2D.Double pt );
343
344 /**
345 * Creates the BufferedImage object that is used to implement double
346 * buffering rendering. This object defines a Graphics2D graphics
347 * context object used to convert from world coordinates
348 * to raster coordinates.
349 */
350 private void buildOffscreen()
351 {
352 Dimension dim = getSize();
353 int w = dim.width;
354 int h = dim.height;
355 raster_area = new Dimension( w, h );
356 Image img = createImage( w, h );
357 goff = img.getGraphics();
358 ioff = (BufferedImage)img;
359 if( g2off != null ) g2off.dispose();
360 g2off = ioff.createGraphics();
361 if( fnt != null )
362 g2off.setFont( fnt );
363 }
364
365 /**
366 * Adjusts the window in world coordinates (world_area) to have the
367 * same aspect ratio between height and width of the canvas (in its
368 * current state).
369 * The adjustment maintains the center of given (raw) window in the same
370 * position. This adjustment may be affected by the canvas vertical
371 * distortion factor (vdistortion).
372 */
373 private void adjustWorld()
374 {
375 double vpr; // canvas viewport aspect ratio
376 double xc, yc; // center of world area
377
378 vpr = (double)raster_area.height / (double)raster_area.width;
379 xc = world_area.x + (world_area.width * 0.5);
380 yc = world_area.y + (world_area.height * 0.5);
381 if( (world_area.height*vdistortion) > (vpr*world_area.width) )
382 world_area.width = (world_area.height*vdistortion) / vpr;
383 else
384 world_area.height = (world_area.width/vdistortion) * vpr;
385 world_area.x = xc - (world_area.width * 0.5);
386 world_area.y = yc - (world_area.height * 0.5);
387
388 double sx, sy, tx, ty; // WC to raster affine transf. parameters
389 sx = (double)raster_area.width / world_area.width;
390 sy = (double)raster_area.height / world_area.height;
391 tx = - (sx * (double)world_area.x);
392 ty = - (sy * (double)world_area.y);
393
394 AffineTransform wc2rc = new AffineTransform();
395 if( y_dir == UPWARD_Y )
396 wc2rc.setTransform( sx, 0.0, 0.0, -sy, tx, raster_area.height - ty );
397 else
398 wc2rc.setTransform( sx, 0.0, 0.0, sy, tx, ty );
399 g2off.setTransform( wc2rc );
400 }
401
402 /**
403 * Sets the limits of the world coordinate window for the target canvas.
404 * The limits are adjusted to be consistent with the canvas
405 * viewport aspect ratio and with the canvas vertical distortion
406 * factor. The adjustment is such that the center of the given
407 * window is maintained.
408 * <p>
409 * The canvas is not automatically repainted by this method.
410 * @param xmin is the left limit of target window
411 * @param xmax is the right limit of target window
412 * @param ymin is the bottom limit of target window if vertical Y axis is upward, otherwise it is the top limit
413 * @param ymax is the top limit of target window if vertical Y axis is upward, otherwise it is the bottom limit
414 * @see #world_area
415 * @see #setVDistortion
416 */
417 public void setWorld( double xmin, double xmax, double ymin, double ymax )
418 {
419 if( world_area == null )
420 {
421 world_raw = new Rectangle2D.Double();
422 world_area = new Rectangle2D.Double();
423 }
424 world_raw.x = world_area.x = xmin;
425 world_raw.y = world_area.y = ymin;
426 world_raw.width = world_area.width = xmax - xmin;
427 world_raw.height = world_area.height = ymax - ymin;
428 if( g2off != null )
429 adjustWorld();
430 }
431
432 /**
433 * Translates the world coordinate window of the target canvas in the
434 * horizontal direction based on a given pan factor.
435 * This factor is a percentage of the current window horizontal
436 * size. A negative factor indicates a window translation to
437 * the right (the client model will move left), and a positive
438 * factor indicates a window translation to the left (the client
439 * model will move right).
440 * <p>
441 * The canvas is not automatically repainted by this method.
442 * @param fac is the pan factor
443 * @see #world_area
444 * @see #setWorld
445 */
446 public void panWorldX( double fac )
447 {
448 double translation; // window horizontal translation
449 double xmin, xmax, ymin, ymax; // new window limits
450
451 translation = world_area.width * fac;
452 xmin = world_area.x - translation;
453 xmax = world_area.x + world_area.width - translation;
454 ymin = world_area.y;
455 ymax = world_area.y + world_area.height;
456 setWorld( xmin, xmax, ymin, ymax );
457 }
458
459 /**
460 * Translates the world coordinate window of the target canvas in the
461 * vertical direction based on a given pan factor.
462 * This factor is a percentage of the current window horizontal
463 * size. If the vertical Y axis is upward, a negative factor indicates
464 * a upward window translation (the client model will move downward),
465 * and a positive factor indicates a downward window translation (the
466 * client model will move upward). If the vertical Y axis is downward,
467 * the reverse will occur.
468 * <p>
469 * The canvas is not automatically repainted by this method.
470 * @param fac is the pan factor
471 * @see #world_area
472 * @see #setWorld
473 */
474 public void panWorldY( double fac )
475 {
476 double translation; // window vertical translation
477 double xmin, xmax, ymin, ymax; // new window limits
478
479 translation = world_area.height * fac;
480 xmin = world_area.x;
481 xmax = world_area.x + world_area.width;
482 ymin = world_area.y - translation;
483 ymax = world_area.y + world_area.height - translation;
484 setWorld( xmin, xmax, ymin, ymax );
485 }
486
487 /**
488 * Scales the world coordinate window of the target canvas based on the
489 * given scaling factor. This factor is a percentage of decrease
490 * (positive factor) or increase (negative factor) of the window sizes.
491 * The result of this inversion (positive ==> decrease, negative ==>
492 * increase) is that the size of the client's enviroment
493 * decreases with a negative factor and increases with a positive
494 * factor.
495 * <p>
496 * The canvas is not automatically repainted by this method.
497 * @param fac is the scaling factor
498 * @see #world_area
499 * @see #setWorld
500 */
501 public void scaleWorld( double fac )
502 {
503 double scalefac = 1.0 + fac; // scale factor from percentage factor
504 double xc, yc; // center of world area
505 double sizex, sizey; // world area new half sizes
506
507 xc = world_area.x + (world_area.width * 0.5);
508 yc = world_area.y + (world_area.height * 0.5);
509 sizex = (world_area.width*0.5) / scalefac;
510 sizey = (world_area.height*0.5) / scalefac;
511 setWorld( xc - sizex, xc + sizex, yc - sizey, yc + sizey );
512 }
513
514 /**
515 * Sets the WC window vertical distortion factor. This factor specifies
516 * the amount of distortion between the height and width of the world
517 * coordinate area. It is essentially a factor between the vertical and
518 * horizontal scales in client world coordinate space. A factor with
519 * value greater than one will cause a vertical stretching of the
520 * image.
521 * <p>
522 * The canvas is not automatically repainted by this method.
523 * @param vdistortion is the vertical distortion factor
524 * @see #vdistortion
525 * @see #setWorld
526 */
527 public void setVDistortion( double vdistortion )
528 {
529 if( vdistortion > 0.0 ) this.vdistortion = vdistortion;
530 }
531
532 /**
533 * Gets the WC window vertical distortion factor.
534 * @return vertical distortion factor
535 * @see #vdistortion
536 * @see #setVDistortion
537 */
538 public double getVDistortion()
539 {
540 return( vdistortion );
541 }
542
543 /**
544 * Transforms the coordinates of a point given in client space (WC) to
545 * device-dependent (raster) coordinates. The canvas device space has
546 * its origin in the upper-left corner of the canvas.
547 * The <code>CanvasWC</code> object contains an {@link AffineTransform}
548 * object that defines how to convert coordinates from client space (world
549 * coordinates) to device-dependent (raster) coordinates in device space.
550 * Note that, to avoid creating new point objects, the method returns a
551 * reference to a private object and not a copy of a new point. If
552 * needed, clients must clone the returned object.
553 * @param world_x is the x coordinate of given point in client space
554 * @param world_y is the y coordinate of given point in client space
555 * @return point in raster (device) coordinates
556 * @see #setWorld
557 * @see #raster2world
558 */
559 public Point world2raster( double world_x, double world_y )
560 {
561 AffineTransform wc2rc = g2off.getTransform();
562 pt_world.x = world_x;
563 pt_world.y = world_y;
564 wc2rc.transform( pt_world, pt_world );
565 pt_raster.x = (int)pt_world.x;
566 pt_raster.y = (int)pt_world.y;
567 return( pt_raster );
568 }
569
570 /**
571 * Transforms the coordinates of a point given in device space (raster) to
572 * client space (world coordinates). The canvas device space has
573 * its origin in the upper-left corner of the canvas.
574 * The <code>CanvasWC</code> object contains an {@link AffineTransform}
575 * object that defines how to convert coordinates from device-dependent
576 * (raster) coordinates in device space to client space (world
577 * coordinates).
578 * Note that, to avoid creating new point objects, the method returns a
579 * reference to a private object and not a copy of a new point. If
580 * needed, clients must clone the returned object.
581 * @param raster_x is the x coordinate of the given point in device space
582 * @param raster_y is the y coordinate of the given point in device space
583 * @return point in world (client) coordinates
584 * @see #setWorld
585 * @see #world2raster
586 */
587 public Point2D.Double raster2world( int raster_x, int raster_y )
588 {
589 AffineTransform wc2rc = g2off.getTransform();
590 pt_world.x = (double)raster_x;
591 pt_world.y = (double)raster_y;
592 try {
593 wc2rc.inverseTransform( pt_world, pt_world );
594 }
595 catch( NoninvertibleTransformException e ) {
596 pt_world.x = 0.0;
597 pt_world.y = 0.0;
598 }
599 return( pt_world );
600 }
601
602 /**
603 * Handles the event of the user pressing down the mouse button.
604 * Calls client method <code>mousePressedWC</code>
605 * @param e is mouse event object
606 * @see #mousePressedWC
607 */
608 public void mousePressed( MouseEvent e )
609 {
610 AffineTransform wc2rc = g2off.getTransform();
611 ept_world.x = (double)e.getX();
612 ept_world.y = (double)e.getY();
613 try {
614 wc2rc.inverseTransform( ept_world, ept_world );
615 }
616 catch( NoninvertibleTransformException error ) {
617 ept_world.x = 0.0;
618 ept_world.y = 0.0;
619 }
620 mousePressedWC( e, ept_world );
621 }
622
623 /**
624 * Handles the event of the user dragging the mouse while holding down the mouse button.
625 * Calls client method <code>mouseDraggedWC</code>
626 * @param e is mouse event object
627 * @see #mouseDraggedWC
628 */
629 public void mouseDragged( MouseEvent e )
630 {
631 AffineTransform wc2rc = g2off.getTransform();
632 ept_world.x = (double)e.getX();
633 ept_world.y = (double)e.getY();
634 try {
635 wc2rc.inverseTransform( ept_world, ept_world );
636 }
637 catch( NoninvertibleTransformException error ) {
638 ept_world.x = 0.0;
639 ept_world.y = 0.0;
640 }
641 mouseDraggedWC( e, ept_world );
642 }
643
644 /**
645 * Handles the event of the user releasing the mouse button.
646 * Calls client method <code>mouseReleasedWC</code>
647 * @param e is mouse event object
648 * @see #mouseReleasedWC */
649 public void mouseReleased( MouseEvent e )
650 {
651 AffineTransform wc2rc = g2off.getTransform();
652 ept_world.x = (double)e.getX();
653 ept_world.y = (double)e.getY();
654 try {
655 wc2rc.inverseTransform( ept_world, ept_world );
656 }
657 catch( NoninvertibleTransformException error ) {
658 ept_world.x = 0.0;
659 ept_world.y = 0.0;
660 }
661 mouseReleasedWC( e, ept_world );
662 }
663
664 // This method required by MouseListener.
665 /**
666 * Dummy method for the event of mouse motion in canvas.
667 * @param e is mouse event object
668 */
669 public void mouseMoved( MouseEvent e ){}
670
671 // This method is required by MouseMotionListener.
672 /**
673 * Dummy method for the event of mouse clicked in canvas.
674 * @param e is mouse event object
675 */
676 public void mouseClicked( MouseEvent e ){}
677
678 // This method is required by MouseMotionListener.
679 /**
680 * Dummy method for the event of mouse exited from canvas.
681 * @param e is mouse event object
682 */
683 public void mouseExited( MouseEvent e ){}
684
685 // This method is required by MouseMotionListener.
686 /**
687 * Dummy method for the event of mouse entered in canvas.
688 * @param e is mouse event object
689 */
690 public void mouseEntered( MouseEvent e ){}
691
692 /**
693 * Repaint method of canvas. It calls method <code>update</code>.
694 * @param g is the target graphics context
695 * @see #update
696 */
697 // public void paint( Graphics g )
698 // {
699 // update( g );
700 // }
701
702 /**
703 * Update method of canvas.
704 * <br>
705 * In case the sizes of the canvas have changed, it (re)builds a
706 * {@link BufferedImage} object (offscreen image) to implement
707 * double buffering rendering. In this case, the world coordinate window
708 * defined by the client (if defined) is adjusted according to the new canvas
709 * sizes.
710 * <br>
711 * Then, it clears the canvas using the currently defined background color and
712 * calls the client's redraw method, passing the {@link Graphics2D} graphics
713 * context object of the offscreen image, so that the client may display
714 * on it.
715 * <br>
716 * Finally, it fills the canvas with the image that was displayed by the canvas
717 * on the offscreen image.
718 * @param g is the target graphics context
719 * @see #setWorld
720 * @see #redraw
721 */
722 public void paintComponent( Graphics g )
723 {
724 if( (raster_area == null) || !raster_area.equals(getSize()) )
725 {
726 buildOffscreen();
727 if( world_area == null )
728 {
729 world_area = new Rectangle2D.Double( 0.0, 0.0,
730 (double)raster_area.width, (double)raster_area.height );
731 world_raw = (Rectangle2D.Double)world_area.clone();
732 adjustWorld();
733 }
734 else
735 {
736 world_area.x = world_raw.x;
737 world_area.y = world_raw.y;
738 world_area.width = world_raw.width;
739 world_area.height = world_raw.height;
740 adjustWorld();
741 }
742 }
743
744 // Clears the offscreen area.
745 g2off.setColor( getBackground() );
746 g2off.fill( world_area );
747
748 // Calls abstract method for redrawing world.
749 redraw( g2off );
750
751 // Draws the buffered image to the screen.
752 g.drawImage( ioff, 0, 0, this );
753 }
754
755 /**
756 * Set font to be used to display texts.
757 */
758 public void setFont( Font fnt )
759 {
760 this.fnt = fnt;
761 if( g2off != null )
762 g2off.setFont( fnt );
763 }
764
765 /**
766 * Set opacity to be used to display texts.
767 */
768 public void setTextOpacity( int text_opacity )
769 {
770 this.text_opacity = text_opacity;
771 }
772
773 /**
774 * Set alignment to be used to display texts.
775 */
776 public void setTextAlignment( int text_alignment )
777 {
778 this.text_alignment = text_alignment;
779 }
780
781 /**
782 * Draw a given string on a given position.
783 * (Parts of this method were stolen from TWFText.java)
784 */
785 public void drawString( String text, double x, double y )
786 {
787 TextLayout text_layout = new TextLayout( text, fnt, frc );
788 Color curr_color = g2off.getColor();
789 int w = (int)text_layout.getAdvance();
790 int as = (int)text_layout.getAscent();
791 int ds = (int)text_layout.getDescent();
792 int dx = 0;
793 int dy = 0;
794
795 switch( text_alignment )
796 {
797 case NORTH_WEST:
798 dy -= as;
799 break;
800 case NORTH:
801 dx -= w/2;
802 dy -= as;
803 break;
804 case NORTH_EAST:
805 dx -= w;
806 dy -= as;
807 break;
808 case SOUTH_WEST:
809 dy += ds;
810 break;
811 case SOUTH:
812 dx -= w/2;
813 dy += ds;
814 break;
815 case SOUTH_EAST:
816 dx -= w;
817 dy += ds;
818 break;
819 case WEST:
820 dy -= (as - ds)/2;
821 /* Deduction:
822 * _Top _
823 * AAAA |
824 * A A | AS
825 * AAAAAA pppp |
826 * A A p p |
827 * A A pppp -BaseLine -
828 * p | DS
829 * p _Bottom _|
830 *
831 * AS is the ascent and DS is the descent of
832 * the text. To center the text in vertical
833 * direction we need to shift upward the
834 * baseline by AS, and downward by (AS+DS)/2.
835 * So: dy = AS - (AS+DS)/2 = (AS-DS)/2.
836 */
837 break;
838 case CENTER:
839 dx -= w/ 2;
840 dy -= (as - ds)/2;
841 // look at "west" case
842 break;
843 case EAST:
844 dx -= w;
845 dy -= (as - ds)/2;
846 // look at "west" case
847 break;
848 default:
849 case BASE_LEFT:
850 //dx = 0.0;
851 //dy = 0.0; // trivial
852 break;
853 case BASE_CENTER:
854 dx -= w/2;
855 break;
856 case BASE_RIGHT:
857 dx -= w;
858 break;
859 }
860
861 /* Finally draw the given text string. This is a little bit
862 * tricky: The (Graphics2D) graphics context will transform
863 * the text string with its current affine transformation
864 * matrix prior to displaying it. To get the text with
865 * its original font size, we temporarely set the current
866 * transformation matrix to the identity matrix, draw the
867 * text in raster coordinates, and restore the correct
868 * affine transformation matrix.
869 */
870 pt_raster = world2raster( x, y );
871 AffineTransform g2off_transf = g2off.getTransform();
872 g2off.setTransform( identity );
873 if( text_opacity == OPAQUE )
874 {
875 text_box.x = pt_raster.x + dx - 4;
876 text_box.y = pt_raster.y - dy - as;
877 text_box.width = w + 7;
878 text_box.height = as + ds;
879 g2off.setColor( getBackground() );
880 g2off.fill( text_box );
881 g2off.setColor( curr_color );
882 }
883 g2off.drawString( text, pt_raster.x + dx, pt_raster.y - dy );
884 g2off.setTransform( g2off_transf );
885 }
886 }